home *** CD-ROM | disk | FTP | other *** search
-
- C O D E O P T I M I Z A T I O N
- An Assembly Guide
- Written by
- D A R K M A N / V L A D
-
-
- -------------------------------------------------
- Use the AL/AX register instead of other registers
- -------------------------------------------------
- Sometimes it is more optimized to use the AL/AX register instead of the
- other registers, comparing with registers could be done like this:
-
- cmp bx,1234h ; Compare BX with 1234h (4 bytes)
-
- A more optimized way of doing this is:
-
- cmp ax,1234h ; Compare AX with 1234h (3 bytes)
-
- However this can ONLY be done when the AL/AX register DOESN'T hold an
- important value, but if the AL/AX register is used many times it COULD
- be more optimized to use it even if you have to PUSH it and then POP it
- afterwards.
-
- ----------------------------------------------
- Use the Data Segment instead of other segments
- ----------------------------------------------
- Moving a value from the memory to AX could be done like this:
-
- mov ax,es:[si] ; Move ES:[SI] to AX (3 bytes)
-
- A more optimized way of doing this is:
-
- mov ax,ds:[si] ; Move DS:[SI] to AX (2 bytes)
-
- ----------------
- Clear a register
- ----------------
- Clearing a register could be done like this:
-
- mov ax,00h ; Clear AX (3 bytes)
-
- A more optimized way of doing this is:
-
- sub ax,ax ; Clear AX (2 bytes)
-
- Or a equally optimized way of this is:
-
- xor ax,ax ; Clear AX (2 bytes)
-
- ---------------------
- Clear the DX register
- ---------------------
- Clearing the DX register could be done like this:
-
- mov dx,00h ; Clear DX (3 bytes)
-
- Or like this:
-
- xor dx,dx ; Clear DX (2 bytes)
-
- A more optimized way of doing this is:
-
- cwd ; Convert word to doubleword (1 byte)
-
- However this can ONLY be done if the AX register is less than 8000h.
-
- ---------------------------
- Test if a register is clear
- ---------------------------
- Testing if a register is clear could be done like this:
-
- cmp ax,00h ; AX = 0? (3 bytes)
-
- A more optimized way of doing this is:
-
- or ax,ax ; AX = 0? (2 bytes)
-
- ----------------------------------------------------
- Use a 16 bit register instead of two 8 bit registers
- ----------------------------------------------------
- Moving a value to a 16 bit register could be done like this:
-
- mov ah,12h ; Move 12h to AH (2 bytes)
- mov al,34h ; Move 34h to AL (2 bytes)
-
- A more optimized way of doing this is:
-
- mov ax,1234h ; Move 1234h to AX (3 bytes)
-
- However this can ONLY be done if the two 8 bit registers are the high and low
- register of the same 16 bit register.
-
- ---------------------------------------------------------
- Move the AL/AX register to another register or vice versa
- ---------------------------------------------------------
- Moving the AL/AX register to another register could be done like this:
-
- mov bx,ax ; Move AX to BX (2 bytes)
-
- A more optimized way of doing this is:
-
- xchg ax,bx ; Exchange AX with BX (1 byte)
-
- However this can ONLY be done if the source registers value is unimportant
- afterwards, because the source register will hold the destination registers
- value.
-
- -------------------------------
- Use DI/SI as base instead of BP
- -------------------------------
- Moving a value from the memory to AX could be done like this:
-
- mov ax,ds:[bp] ; Move DS:[BP] to AX (3 bytes)
-
- A more optimized way of doing this is:
-
- mov ax,ds:[si] ; Move DS:[SI] to AX (2 bytes)
-
- If the DI/SI index is used many times it COULD be more optimized to used
- the DI/SI index even if you have to PUSH it and then POP it afterwards.
-
- --------------------------------------------------------
- Use the CMPS, LODS, MOVS, SCAS, STOS and REP instuctions
- --------------------------------------------------------
- Moving a value from the memory to AX could be done like this:
-
- mov ax,ds:[si] ; Move DS:[SI] to AX (2 bytes)
-
- A more optimized way of doing this is:
-
- lodsw ; Load AX with DS:[DI] (1 bytes)
-
- Remember to clear/set the direction flag. Sometimes it COULD be more
- optimized to use these instructions even if you have to PUSH and then POP the
- used registers/segments afterwards.
-
- ---------------------------
- Move a segment to a segment
- ---------------------------
- Moving a segment to a segment is weird, because you can't move a segment to
- another segment directly by:
-
- mov ds,cs ; Can't do this!
-
- Because of that, you could use a register as a temporary storage, like this:
-
- mov ax,cs ; Move CS to AX (2 bytes)
- mov ds,ax ; Move AX to DS (2 bytes)
-
- But if an important value is held by the AX register, you have to PUSH and
- the POP it afterwards, that would add the code by 2 bytes, a more optimized
- way of doing this is:
-
- push cs ; Save CS at stack (1 byte)
- pop ds ; Load DS from stack (CS) (1 byte)
-
- -----------------------------------------------
- Use SHL/SHR instead of the DIV/MUL instructions
- -----------------------------------------------
- Multiplying AL with 2 could be done like this:
-
- mov bh,02h ; Move 02h to BH (2 bytes)
- mul bh ; Multiply AL with BL (2 bytes)
-
- A more optimized way of doing this is:
-
- shl al,01h ; Multiply AL with 02h (2 bytes)
-
- This can ONLY be used if the source can be divided by 2.
-
-
- ----------------------------------------
- Use object codes instead of instructions
- ----------------------------------------
- A far call could be done like this:
-
- call far address ; Make a far call (3 bytes)
- address dd ? ; Address of a procedure (4 bytes)
-
- A more optimized way of doing this is:
-
- callfar db 9ah ; Object code of a far call (1 byte)
- address dd ? ; Address of a procedure (4 bytes)
-
- This will ONLY optimize your code if the immediate value after the object
- code is a word or greater.
-
- --------------
- Use procedures
- --------------
- If some code is used many times and it's size is large, it COULD be more
- optimized to use it as a procedure, the following formula can calculate if
- it is more optimized to use a procedure:
-
- Bytes saved = (procedure size - 4) * number of invocations - procedure size
-
- Figure 4 in the parentheses of the formula is there because the size of the
- CALL and RET instructions together are 4 bytes.
-
- ------------------------
- Make procedures flexible
- ------------------------
- Make procedures flexible if possible, this CAN optimize your code, because
- the redundant code is removed, a example from this is:
-
- movefptrend proc near ; Move file pointer to the end
- mov al,02h ; " " " " "
- movefileptr proc near ; Move file pointer to end/beginning
- cwd ; Convert word to doubleword
- movefpointer proc near ; Move file pointer to a offset
- xor dx,dx ; Clear DX
- mov ah,42h ; Move file pointer
- int 21h ; Do it!
- ret ; Return!
- endp
- endp
- endp
-
- Use the formula from above to calculate if the flexible procedures can be
- used to optimize your code.
-
- ----------------------------------
- Use all the information in the DTA
- ----------------------------------
- DTA (Disk Transfer Area) is used by service 4eh and 4fh in the DOS functions
- interrupt (INT 21h), the contents of the DTA block are:
-
- ----------------------------------------
- Offset Size Contents
- ----------------------------------------
- 00 Byte Drive letter
- 01-0B Bytes Search template
- 0C-14 Bytes Reserved
- 15 Byte File attribute
- 16-17 Word File time
- 18-19 Word File date
- 1A-1D DWord File size
- 1E-3A Bytes ASCIIZ filename + extension
- ----------------------------------------
-
- - If your want to reset the files time and date afterwards, then use the
- DTA instead of service 57h in INT 21h.
- - If you only want to infect one file, then change the drive letter to a
- illegal value instead of making redundant code in the exit part of the
- program, if you change the drive letter to a illegal value an error will
- occur.
-
- However, this will ONLY optimize your code, if you are already using the DTA.
-
- ---------------------
- Final tips and tricks
- ---------------------
- - Remove all unnecessary NOPs
- - Move your code around, to see if some JUMP NEAR instructions could be
- replaced by JUMP SHORT instructions etc.
- - Don't use instructions to calculate values that can be calculated
- directly in a parentheses.
- - Use LEA instead of MOV OFFSET.
- - Use the stack to store temporary data, but be careful if it is a COM file.
- - Use the CBW instructions to clear the AH register if AL is less than 80h.
- - Use DEC/INC instead of ADD/SUB register,01h.
- - When using DEC/INC it is more optimized to use 16 bits registers rather
- than 8 bit registers.
-
-
-
-